package form.ui.component.Select {
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.utils.Timer;
	
	import form.events.FormElementChangeEvent;
	import form.events.OptionEvent;
	import form.events.ScrollBarEvent;
	import form.events.ScrollButtonEvent;
	import form.ui.Select;
	import form.ui.component.ScrollBar.ScrollBar;
	import form.ui.component.ScrollBar.ScrollButton;
	import form.ui.component.ScrollBar.ScrollDown;
	import form.ui.component.ScrollBar.ScrollUp;
	import form.ui.core.FMSprite;
	import form.ui.core.Frame;
	import form.ui.core.Stack;
	import form.utils.EventUtils;
	import form.utils.HierarchyUtils;
	import form.utils.MathUtils;
	
	//import mx.containers.Form;

	public class DropDown extends Frame {
		public static const DEFAULT_MAX_HEIGHT:int = 160;
		
		public static const FILL_COLOR:int = 0xffffff;
		public static const FILL_OPACITY:Number = 1;
		
		public static const STROKE_COLOR:int = 0xbbbbbb;
		public static const STROKE_THICKNESS:int = 1;
		
		public static const CLOSE_DELAY:int = 1300;
		
		protected var stack:Stack = new Stack();
		protected var overlay:FMSprite = new FMSprite;
		
		protected var scrollUpButton:ScrollButton = new ScrollUp();
		protected var scrollBar:ScrollBar = new ScrollBar();
		protected var scrollDownButton:ScrollButton = new ScrollDown();
		
		protected var select:Select;
		
		protected var closeTimer:Timer = new Timer(DropDown.CLOSE_DELAY);
		
		protected var addOnClose:Array = new Array();
		
		private var _maxHeight:int = DropDown.DEFAULT_MAX_HEIGHT;
		
		public function DropDown(select:Select) {
			super();
			
			this.select = select;
			
			DropDown.defineDropDown(this);
			DropDown.defineOverlay(this.overlay);
			
			this.close();
			
			this.addChild(this.stack);
			this.addChild(this.overlay);
			
			this.addChild(this.scrollUpButton);
			this.addChild(this.scrollDownButton);
			this.addChild(this.scrollBar);
			
			this.height = this.stack.height;
			
			closeTimer.repeatCount = 1;
			closeTimer.addEventListener(TimerEvent.TIMER, this.onCloseTimerEvent);
			
			this.addEventListener(MouseEvent.ROLL_OUT, this.onMouseOut);
			this.addEventListener(MouseEvent.ROLL_OVER, this.onMouseOver);
			
			this.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp, true);
			
			this.addEventListener(ScrollBarEvent.SCROLLED, this.onScrolled);
			this.addEventListener(ScrollButtonEvent.SCROLL_UP, this.onScrollUp);
			this.addEventListener(ScrollButtonEvent.SCROLL_DOWN, this.onScrollDown);
			
			this.addEventListener(OptionEvent.SELECTED, this.onOptionSelected);
			this.addEventListener(OptionEvent.SHOW, this.onShowOption);
		}
		
		public function get maxHeight():int { 
			return this._maxHeight;
		}
		
		public function set maxHeight(value:int):void {
			this._maxHeight = value;
		}
		
		public function get isOpen():Boolean {
			return this.visible;
		}
		
		public function open():void {
			if (this.parent != null) { this.parent.removeChild(this); }
			 			
			if (this.select.stage != null) {
				this.select.stage.addChild(this);
				
				var global:Point = this.select.localToGlobal(new Point(0, this.select.height));
				this.coordinates = [global.x, global.y];
				this.width = this.select.width;
			} 
			
			if (this.stack.height > this.height) {
				this.scrollUpButton.visible = true;
				
				this.scrollBar.handleHeight = this.height / this.stack.height;
				this.scrollBar.handleValue = 0;
				this.scrollBar.visible = true;
				
				this.scrollDownButton.visible = true;
			} else {
				this.scrollUpButton.visible = false;
				this.scrollBar.visible = false;
				this.scrollDownButton.visible = false;
			}
			this.scrollTo(0);
						
			this.visible = true;
		}
		
		public function close():void {
			if (this.parent != null) { this.parent.removeChild(this); }
			
			this.visible = false;
			
			var length:int = this.addOnClose.length;
			for (var i:int = 0; i < length; i++) {
				this.addToStack(this.addOnClose[i]);
			}
			this.addOnClose = new Array();
		}
		
		public function clear():void {
			if (this.isOpen) {
				this.close();
			}
			
			var length:int = this.stack.numChildren;
			for (var i:int = 0; i < length; i++) {
				this.stack.removeChildAt(this.stack.numChildren - 1);
			}
			
			this.height = Math.min(this.stack.height, this.maxHeight);
		}
		
		public function addOption(text:String, value:String):void {
			var option:Option = new Option();
			option.text = text;
			option.value = value;
			
			this.addToStack(option);
		}
		
		public function selectOption(option:Option):void {
			var length:int = this.stack.numChildren;
			for (var i:int = 0; i < length; i++) {
				var child:DisplayObject = this.stack.getChildAt(i);
				
				if (child is Option) {
					if (child != option) {
						Option(child).enable();
					} else {
						Option(child).disable();
					}
				}
			}
		}
		
		public function unselectAllOptions():void {
			this.selectOption(null);
		}
		
		public function findOptionByValue(value:String):Option {
			var length:int = this.stack.numChildren;
			for (var i:int = 0; i < length; i++) {
				var child:DisplayObject = this.stack.getChildAt(i);
				
				if (child is Option && Option(child).value == value) {
					return Option(child);
				}
			}
			
			return null;
		}
		
		public function findOptionByText(text:String):Option {
			var length:int = this.stack.numChildren;
			for (var i:int = 0; i < length; i++) {
				var child:DisplayObject = this.stack.getChildAt(i);
				
				if (child is Option && Option(child).text == text) {
					return Option(child);
				}
			}
			
			return null;
		}
		
		public function addHeader(text:String):void {
			var header:Header = new Header();
			header.text = text;
			
			this.addToStack(header);
		}
		
		public function addSeparator():void {
			var separator:Separator = new Separator();
			
			this.addToStack(separator);
		}
		
		protected function addToStack(object:DisplayObject):void {
			if (this.isOpen) {
				this.addOnClose.push(object);
				return;
			}
			
			this.stack.addChild(object);
			
			this.height = Math.min(this.stack.height, this.maxHeight);
		}
		
		public override function set width(value:Number):void {
			super.width = value;
			this.overlay.width = this.width;
			
			this.scrollUpButton.x = this.width - this.scrollUpButton.width - DropDown.STROKE_THICKNESS;
			this.scrollBar.x = this.width - this.scrollBar.width - DropDown.STROKE_THICKNESS;
			this.scrollDownButton.x = this.width - this.scrollDownButton.width - DropDown.STROKE_THICKNESS;
		}
		
		public override function set height(value:Number):void {
			super.height = value;
			this.overlay.height = this.height + DropDown.STROKE_THICKNESS * 2;
			
			this.scrollUpButton.y = 0;
			
			this.scrollBar.y = this.scrollUpButton.height;
			this.scrollBar.height = this.height - this.scrollUpButton.height - this.scrollDownButton.height - DropDown.STROKE_THICKNESS;
			
			this.scrollDownButton.y = this.scrollUpButton.height + this.scrollBar.height;
		}
		
		protected function scrollTo(value:Number):void {
			value = MathUtils.constrain(value, 0, 1);
			
			this.stack.y = -(this.stack.height - this.height) * value;
		}
		
		protected function scrollUp(pixels:Number):void {
			this.scrollDown(-pixels);
		}
		
		protected function scrollDown(pixels:Number):void {
			var y:int = this.stack.y - pixels;
			
			if (y > 0) { y = 0; }
			var max:int = -this.stack.height + this.height;
			if (y < max) { y = max; }
			
			this.stack.y = y;
			
			this.scrollBar.handleValue = y / max;
		}
		
		protected static function defineDropDown(dropDown:DropDown):void {
			dropDown.fillColor = DropDown.FILL_COLOR;
			dropDown.fillOpacity = DropDown.FILL_OPACITY
			
			dropDown.strokeThickness = 0;
			dropDown.strokeOpacity = 0;
		}
		
		protected static function defineOverlay(overlay:FMSprite):void {
			overlay.mouseEnabled = false;
			
			overlay.y = -DropDown.STROKE_THICKNESS * 2;
			
			overlay.fillOpacity = 0;
			overlay.strokeOpacity = 1;
			
			overlay.strokeColor = DropDown.STROKE_COLOR;
			overlay.strokeThickness = DropDown.STROKE_THICKNESS * 2;
		}
		
		public function onMouseOut(e:MouseEvent):void {
			if (this.scrollBar.dragging) { return; }
			
			if (e.relatedObject != null && (
				e.relatedObject == this || HierarchyUtils.isAncestor(this, e.relatedObject) || 
				e.relatedObject == this.select || HierarchyUtils.isAncestor(this.select, e.relatedObject)
			)) { return; }
			
			this.closeTimer.reset();
			this.closeTimer.start();
		}
		
		public function onMouseOver(e:MouseEvent):void {
			this.closeTimer.stop();
		}
		
		public function onMouseUp(e:MouseEvent):void {
			if (
				e.target == null || 
				(e.target == this.scrollUpButton || HierarchyUtils.isAncestor(this.scrollUpButton, DisplayObject(e.target))) ||
				(e.target == this.scrollBar || HierarchyUtils.isAncestor(this.scrollBar, DisplayObject(e.target))) ||
				(e.target == this.scrollDownButton || HierarchyUtils.isAncestor(this.scrollDownButton, DisplayObject(e.target)))
			) { return; }
			
			if (this.isOpen) {
				this.close();
			}
		}
		
		protected function onCloseTimerEvent(e:TimerEvent):void {
			if (this.isOpen) {
				this.close();
			}
		}
		
		protected function onOptionSelected(event:OptionEvent):void {
			this.onShowOption(event);
			
			var e:FormElementChangeEvent = new FormElementChangeEvent(FormElementChangeEvent.SELECT_OPTION_SELECTED);
			e.element = this.select;
			
			this.select.dispatchEvent(e);
		}
		
		protected function onShowOption(event:OptionEvent):void {
			EventUtils.stopPropagation(event);
			
			if (this.isOpen) {
				this.close();
			}
			
			select.text = event.text;
			select.formValue = event.value;
			
			this.selectOption(event.option);
		}
		
		protected function onScrolled(e:ScrollBarEvent):void {
			this.scrollTo(e.value);
			
			EventUtils.stopPropagation(e);
		}
		
		protected function onScrollUp(e:Event):void {
			this.scrollUp(ScrollButton.MOVE);
		}
		
		protected function onScrollDown(e:Event):void {
			this.scrollDown(ScrollButton.MOVE);
		}	
	}
}